        .PAGE
;--------------------------------------------------------------------------
;  Monitor code - first displays requested icons, error codes or messages,
;  and then outputs menu of options to user and awaits input
;--------------------------------------------------------------------------

INITMON				  ;entry point for displays
        .IF  ROM4K = 0
        BSR     SAVEREGS	  ;save registers
        CLR.L   D7		  ;reset reg for error indicators
        CLR.B   STATFLGS	  ; and status flags
        BSET    #NOCONT,STATFLGS  ;set external entry indicator

INIT1
        MOVEM.L D0/A2-A3,-(SP)	  ;save incoming arguments
        BSR     DSABLDSK	  ;disable ints from both drives
        BSR     RSTKBD		  ;reset keyboard
        BSR     CLRRST		  ;and clear reset
        BSR     CursorInit	  ;init cursor and mouse
        MOVEM.L (SP)+,D0/A2-A3	  ;restore arguments

INIT2			        ;internal entry point
        .IF  DEBUG = 0
        MOVEA   #STKBASE,SP     ;reset stack pointer		        RM000
        .ENDC

        .IF  USERINT = 1
        MOVEM.L D0/A2-A3,-(SP)  ;save incoming arguments
        .ENDC
        .ENDC		        ;{ROM4K}

        BSR     SETVLTCH        ;set video latch

        .IF  USERINT = 0
        BSR     CLRSCRN	        ;go clear screen
        MOVEQ   #FIRSTROW,D5    ;set initial cursor ptrs
        MOVEQ   #FIRSTCOL,D6
        .ELSE
        BSR     DRAWDESK        ;display the desktop
        BSR     MAKEALERT       ;draw alert box (in case no icon display)

INIT3   MOVEM.L (SP)+,D0/A2     ;restore arguments

        MOVE.L  A2,D1	        ;icon display?
        BEQ.S   @0	        ;skip if no
        BSR     DSPALRTICON     ;go do icon display

@0      TST     D0	        ;error code display?
        BEQ.S   @2	        ;skip if no
        MOVE.L  A2,D1	        ;icon displayed?
        BNE.S   @1	        ;skip if yes
        MOVEQ   #MSGROW,D5      ;else display error code on same line
        MOVEQ   #CODECOL,D6     ; as error msg
        BSR     DSPDEC	        ;display as decimal #
        BRA.S   @2

@1      BSR     DSPCODE	        ;output error code under icon

@2      MOVE.L  (SP)+,A3        ;restore msg ptr
        .ENDC

        MOVE.L  A3,D0	        ;message display?
        BEQ.S   MONITOR

        .IF  USERINT = 0
        BSR     DSPMSGR	        ;go display it
        .ELSE
        BSR     DSPALRTMSG      ;go display message in alert box
        .ENDC

MONITOR			        ;entry point for no screen setup
        .IF  ROM4K = 0
        ORI     #$0700,SR       ;disable all interrupts
        BSR     SETVCTRS        ;set vectors for ROM space	        CHG028
        .ELSE
@1      NOP		        ;hang for 2716 version of ROM
        BRA.S   @1
        .ENDC

        .IF  ROM4K = 0
;---------------------------------------------------------------------------
;  Now output first level menu, prompt line and cursor.	 Do preliminary
;  check to see if CONTINUE option can be displayed.  This is the Customer
;  mode level of the monitor code.
;---------------------------------------------------------------------------

LEVEL1
        .IF  USERINT = 1
        CLR     RECTCNT	        ;clear active rectangle count
        ANDI.B  #$0F,STATFLGS   ;init flags
        BSET    #BTN,STATFLGS   ;set operating with buttons flag

        BTST    #NOCONT,STATFLGS ;display continue?
        BNE.S   OTHRBTNS        ;skip if no
        MOVE.L  STATUS,D0       ;get test status
        ANDI.L  #CONTMSK,D0     ;mask don't cares
        BNE.S   @1	        ;skip if error that disallows continuing
        TST     BOOTMEM	        ;check boot memory area for R/W errors
        BNE.S   @1	        ;skip if any errors

        MOVE    #BTN2STRT,A1    ;display CONTINUE button
        MOVE.B  #KEY2,D0        ;with alternate keycode
        LEA     CONTMSG,A3      ;and description
        MOVEA   #BTN2MSG,A2     ;and location			        RM000
        CLR.L   D1	        ;don't append '...' string
        BSR     MAKEBUTN
        BRA.S   OTHRBTNS        ;and go make other buttons
@1      BSET    #NOCONT,STATFLGS ;set indicator for no CONTINUE option

OTHRBTNS
        .ENDC

DOMENU
        .IF  USERINT = 0
        LEA     LEV1MSG,A3      ;display rest of menu line and get input
        BSR     WRTMENU
        .ELSE
        BTST    #NORSTRT,STATFLGS ;display RESTART button?
        BNE.S   @1		  ;skip if not
        MOVE    #BTN1STRT,A1	  ;else do display
        MOVE.B  #KEY1,D0
        LEA     RTRYMSG,A3
        MOVEA   #BTN1MSG,A2     ;				        RM000
        CLR.L   D1	        ;don't append '...' string
        BSR     MAKEBUTN

@1      MOVE    #BTN3STRT,A1    ;display STARTUP button
        MOVE    #KEY3,D0
        LEA     STRTMSG,A3
        MOVEA   #BTN3MSG,A2     ;				        RM000
        MOVEQ   #-1,D1	        ;append '...' string
        BSR     MAKEBUTN

;	 MOVE.L	 #KBDBFR,KBDQPTR ;init queue ptr

        BSR     CursorDisplay   ;display mouse cursor
        BSET    #CHKCMD,STATFLGS ;require user keyboard input to be prefaced
                                 ; by the CMD key
GETL1   BSR     GETINPUT        ;and go wait for input
        BCS     GETERR	        ;exit if error
        BSR     CursorHide      ;remove cursor from screen

        .ENDC


;  Check if input valid.  If invalid, beep speaker.

        CMP.B   #KEY3,D0        ;alternate boot?
        BNE.S   @2

        .IF  USERINT = 0
        LEA     DVCEMSG,A3      ;request input
        BSR     WRTBOX1
        BSR     READKEY	        ;get input
        CMP.B   #CMDKEY,D0      ;command key?
        BEQ.S   @1	        ;continue if yes
        BSR     CLRBOX	        ;else clear box
        BRA.S   GETL1XIT        ;and exit
@1      BSR     READKEY	        ;get device input

        .IF     NEWTWIG = 1
        BSR     XLATE	        ;translate to boot id and save
        .ELSE
        MOVE.B  D0,BOOTDVCE     ;save boot device keycode
        BSET    #ALTBOOT,D7     ;set alternate boot indicator
        .ENDC

        BRA     DOBOOT	        ;and go attempt boot

        .ELSE
        BSR     CLRDESK	        ;close the alert box
        BRA     BOOTMENU        ;and go display boot menu
        .ENDC

@2      BTST    #NORSTRT,STATFLGS ;RESTART button displayed?
        BNE.S   CONTCHK		  ;skip if not
        CMP.B   #KEY1,D0        ;retry?
        BNE.S   CONTCHK	        ;skip if not

DORESET
        CLR.L   D7	        ;clear error reg		        RM000
        TST.B   SETUPON	        ;turn on setup bit		        RM000
        BRA     BEGIN3	        ;and restart diags		        RM000

CONTCHK
        .IF  USERINT = 1
        BTST    #NOCONT,STATFLGS ;continue option displayed?
        BNE.S   @4	        ;skip if not
        CMP.B   #KEY2,D0        ;continue option selected?
        BNE.S   @4

;  continue from point of failure

        BSR     CLRDESK	        ;clear desktop			        CHG008
        ANDI.L  #ALTBMSK,D7     ;erase error indicators
        MOVE.L  STATUS,D0       ;get power-up status
        BTST    #MMU,D0	        ;MMU error?
        BNE     VIA2TST	        ;yes - continue from VIA tests

        ANDI.L  #BOOTMSK,D0     ;check if error that continues to boot attempt
        BEQ     BOOTCHK	        ;skip if yes
        MOVE.L  D0,-(SP)        ;else save status
        BSR     MAKETEST        ;make test window

;  do init for continue to other tests

        MOVE.B  #$70,D0	        ;turn off mouse
        BSR     COPSCMD
        MOVE.L  (SP)+,D0        ;restore status

        BTST    #VID,D0	        ;serial # error?
        BEQ.S   @1	        ;skip if not
        MOVEA   #CPUSTRT,A1     ;display CPU icon
        BSR     INVICON
        BRA     PARTST	        ;continue with parity test

@1      LSR.L   #7,D0	        ;skip other CPU errors
        TST.B   D0	        ;clock error?
        BNE     CONFIG	        ;yes - continue with config check

        LSR.L   #2,D0
        TST.B   D0	        ;RS232 error?
        BEQ.S   @2	        ;skip if not
        MOVEA   #IOSTRT,A1      ;else display I/O board icon
        BSR     INVICON
        BRA     DSKTST	        ;cont with disk test

@2      TST.B   PARON	        ;must be memory error - reenable parity
        BRA     IOTST	        ; and continue with I/O board testing

        .ENDC
@4
        .IF  USERINT = 0
        CMP.B   #CMDKEY,D0      ; command?
        BNE.S   GETL1XIT
        BSR     ReadKey	        ; go get next input
        .ENDC

        CMP.B   #SKEY,D0        ; service mode desired?
        BEQ     LEVEL2	        ; skip if yes

;  Indicate invalid by beeping speaker

GETL1XIT
        BSR     SQUAWK	        ; sorry charlie
        .IF  USERINT = 0
        BRA.S   LEVEL1	        ; go wait for another try
        .ELSE
LEV1LOOP
        BSR     CursorDisplay   ;redisplay cursor
        BRA.S   GETL1	        ;go get more input

;  Error exit - go output error and return to level 1

GETERR
        LEA     IOBRD,A2        ;get I/O board icon
        SUBA.L  A3,A3	        ;no error message
        BRA     INIT2
        .ENDC

        .ENDC		        ;{ROM4K}
        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to clear video page of memory or write arbitrary long word
;  pattern to entire screen (WRTSCRN entry point).
;-------------------------------------------------------------------------

CLRSCRN CLR.L   D0	        ; write 0's for white screen
WRTSCRN			        ; entry pt for write to screen (assumes D0 set)
        MOVE.L  SCRNBASE,A0     ; get screen base address
        MOVE    #HEX8K-3,D1     ; set longs count
@1      MOVE.L  D0,(A0)+	   ; clear for video page
        DBF     D1,@1
        RTS

        .IF  ROM4K = 0
        .IF  USERINT = 0
;-------------------------------------------------------------------------
;  Subroutine to clear "dialog box" in display row 1.
;-------------------------------------------------------------------------

CLRBOX  MOVEM.L D5-D6/A0,-(SP)  ;save cursor ptrs and working reg
        MOVEQ   #1,D5	        ;clear from 1,0 to 3,0
        MOVEQ   #0,D6
        BSR     SETCRSR	        ;get address for start
        MOVE.L  A6,A0	        ;save
        MOVEQ   #2,D5	        ;get ending address  in A6
        BSR     SETCRSR
CLRIT   CLR     (A0)+	        ;do clear
        CMPA.L  A0,A6
        BNE.S   CLRIT
        MOVEM.L (SP)+,D5-D6/A0  ;restore and
        RTS		        ; exit

;-------------------------------------------------------------------------
;  Subroutine to display menu line
;-------------------------------------------------------------------------

WRTMENU MOVEM.L D5-D6,-(SP)     ;save current cursor ptrs
        CLR     D5	        ;set menu line ptrs
        MOVEQ   #5,D6
        BSR     DSPMSG
        BSR     SETCUR	        ;draw cursor
        BSR     DRWLINE	        ;and underline
        BSR     ReadKey	        ;go wait for input
        BSR     CLRCUR	        ;clear cursor
        MOVEM.L (SP)+,D5-D6
        RTS

;-------------------------------------------------------------------------
;  Draw underline subroutine
;-------------------------------------------------------------------------

DRWLINE MOVEM.L D0/D5-D6,-(SP)  ;save cursor ptrs and working reg
        ADDQ    #1,D5	        ;draw line just above next row
        MOVEQ   #0,D6
        BSR     SETCRSR	        ;get address
        SUBA.L  #180,A6	        ;decrement to bottom of last line
        MOVEQ   #45,D0	        ;set loop count
DRWIT   MOVE    #$FFFF,(A6)+    ;draw black line
        SUBQ    #1,D0
        BNE.S   DRWIT
        MOVEM.L (SP)+,D0/D5-D6  ;restore and
        RTS		        ; exit

;-------------------------------------------------------------------------
;  Subroutine to write to dialog box
;-------------------------------------------------------------------------

WRTBOX1 MOVEM.L D0/D5-D6,-(SP)  ;save D0 and current cursor ptrs
        MOVEQ   #1,D5	        ;set box 1 coordinates
        CLR.L   D6
        BSR     SETCRSR	        ;get address in A6
        MOVE    #RLONGS,D0
@1      CLR.L   (A6)+	        ;clear box
        SUBQ    #1,D0
        BNE.S   @1
        MOVEQ   #1,D6	        ;space over for neatness
        BSR     DSPMSG	        ;write msg
        BSR     DRWLINE
        MOVEM.L (SP)+,D0/D5-D6  ;restore
        RTS		        ;and exit

        .ENDC
        .PAGE
        .IF  USERINT = 0
;-------------------------------------------------------------------------
;  Subroutine to read keycode from COPS - returns down transitions in D0
;-------------------------------------------------------------------------

ReadKey
        BSR     ReadCOPS
        TST.B   D0	        ;ignore "up" transitions
        BPL.S   ReadKey
        RTS		        ;exit with data

        .ELSE
;-------------------------------------------------------------------------
;  Subroutine to read keycode from COPS - returns down transitions in D0
;-------------------------------------------------------------------------

ReadKey
        BSR     WT4INPUT
        TST.B   D0	        ;ignore "up" transitions and mouse data
        BPL.S   ReadKey
        RTS		        ;exit with data

        .ENDC

;-------------------------------------------------------------------------
;  Subroutine to beep speaker for invalid input
;-------------------------------------------------------------------------

SQUAWK  MOVEQ   #$20,D0	        ; set frequency
        MOVE    #250,D1	        ; 1/8 sec duration
        MOVEQ   #4,D2	        ; low volume
        BSR     TONE	        ; and go do it
        RTS

;-------------------------------------------------------------------------
;  Subroutine to convert keycodes to Ascii
;  Inputs:  D0 = keycode (word)
;  Outputs: D0 = Ascii (byte) or =2 if input invalid
;-------------------------------------------------------------------------

KeyToAscii
        MOVEM.L D1/A0,-(SP)     ;save regs
        LEA     AsciiTable,A0   ;keycode to ascii table
        MOVE    D0,D1	        ;keycode to convert
        ANDI    #$007F,D1       ;ensure valid
        SUBI    #32,D1	        ;decrement for table		        RM000
        BPL.S   @1	        ;skip if valid			        RM000
        MOVEQ   #2,D0	        ;else set for invalid char	        RM000
        BRA.S   @2	        ;				        RM000
@1      MOVE.B  0(A0,D1.W),D0   ;get ascii
@2      MOVEM.L (SP)+,D1/A0     ;restore
        RTS		        ;exit


        .PAGE
;-------------------------------------------------------------------------
;  Monitor level 2 (Service mode) - enables access to memory and disk
;-------------------------------------------------------------------------

LEVEL2
        .IF  USERINT = 0
        BSR     CLRSCRN	        ;clear screen
        MOVE    #$0201,CRTROW   ;set starting display ptrs
        .ELSE
        BSR     CLRDESK	        ;display the desktop

        .IF  BMENU = 0
        MOVEA   #MENULOC,A1     ;set up menu start point	        RM000
        BSR     GETROWCOL
        LEA     MENUHDG,A3      ;and display it
        BSR     DSPMSG
        SUB.W   #91,A1	        ;decrement start pt by 1 row + 1 byte
        LEA     MENUHDG,A3      ;get length of menu heading
        BSR     GETLENGTH
        ADDQ    #1,D2	        ;add an extra byte
        ANDI.B  #$FE,D2	        ;ensure its even
        MOVE    D2,D0	        ;save as width of menu heading "box"
        MOVEQ   #14,D1	        ;set height for "box"
        MOVEQ   #-1,D2	        ;set fill pattern
        BSR     INVERSE	        ;go hilite it
        .ENDC

;  make window for output, and display menu line and pull down menu

        BSR     MAKESVCW        ;output service window
DSPMENU BSR     WRTMENU

;  do final initialization and await input

        BSR     CursorDisplay   ;display cursor

;----------------------------------------------------------------------
;  Program NMI key
;
;	 MOVEQ	 #$5A,D0	 ;set / key for NMI
;	 BSR	 COPSCMD
;	 MOVEQ	 #$61,D0
;	 BSR	 COPSCMD
;----------------------------------------------------------------------

GETLEV2
        BSR     GETINPUT        ;and go await input
        BCS     GETERR	        ;exit if error
        BSR     CursorHide      ;else remove cursor from screen and go
                                ; analyze input

        .ENDC

        .IF  USERINT = 0
DSPMENU
        LEA     LEV2MSG,A3      ;output menu and get input
        BSR     WRTMENU
        .ENDC

;  Check for valid input

        CMP.B   #KEY1,D0        ; display memory?
        BEQ     DSPMEM

        CMP.B   #KEY2,D0        ; set memory?
        BEQ     SETMEM

        CMP.B   #KEY3,D0        ; call routine
        BEQ     CALLRTN

        .IF  ROM16K = 1
        CMP.B   #KEY4,D0        ; loop?
        BEQ     LOOPTST
        .ENDC

        CMP.B   #KEY5,D0        ; video adjust?
        BEQ     VIDAJST

        .IF  BURNIN = 1
        CMP.B   #KEY6,D0        ; power cycle?
        BEQ     PowerCycle
        .ENDC

        CMP.B   #KEY7,D0        ; quit?
        BNE.S   @1
        BCLR    #NORSTRT,STATFLGS ;clear no reset status flag
        CLR.L   D0		  ;set parms for level1 - no error code
        SUBA.L  A2,A2		  ;no icon display
        SUBA.L  A3,A3		  ;no message display
        BRA     INIT2		  ;and go back to level1

@1      BRA     INVALID	        ; else invalid input

        .PAGE
        .IF  USERINT = 1
;---------------------------------------------------------------------------
;  Routine to display the preliminary pull-down menu
;---------------------------------------------------------------------------

WRTMENU
        .IF  BMENU = 0
        MOVEQ   #MITEMS,D1      ;set # of items in menu
        LEA     DISPMSG,A3      ;set ptr to menu entries
        CLR     RectCnt	        ;clear active rectangle count
        ANDI.B  #$07,STATFLGS   ;init flags
        BSET    #MENU,STATFLGS  ;set working with menu flag
        MOVE.L  D1,D4	        ;save item count
        MOVEQ   #MENUWIDTH,D0   ;set menu parms
        MULU    #MENULEN,D1     ;length depends on # of items
        ADDQ    #2,D1	        ;incr for bottom border
        MOVE    #MENUSTRT,A1    ;set start point for menu "box"
        MOVE    #MENU1MSG,A2    ;display menu items
        LEA     MENUID,A4       ;ptr to id's for menu entries
        BSR     MAKEMENU        ;go do it

        .ELSE
        CLR     RectCnt	        ;clear active rectangle count
        ANDI.B  #$0F,STATFLGS   ;init flags
        MOVEQ   #MENUWIDTH,D0   ;set menu parms
        MOVEQ   #MITEMS,D1      ;set # of items in menu
        MULU    #MENULEN,D1     ;length depends on # of items
        LEA     MENUHDG,A3      ;set ptr for menu heading
        BSR.S   DSPMENUBOX      ;go display blank menu box w/ heading

        MOVEQ   #MITEMS,D4      ;set # of items in menu
        MOVE    #MENUSTRT,A1    ;set menu starting point
        MOVE    #MENU1MSG,A2    ;menu items display address
        LEA     DISPMSG,A3      ;set ptr to menu entries
        LEA     MENUID,A4       ;ptr to id's for menu entries
        BSR     MAKEMENU        ;go fill in the menu

        .ENDC		        ;{MENU}

        RTS

        .IF  BMENU = 1
;---------------------------------------------------------------------------
;  Subroutine to display blank menu box with heading
;  Inputs:
;       D0 = menu width
;       D1 = menu length
;       A3 = menu heading
;  Outputs:
;       None
;  Side Effects:
;       D2/A1,A3 trashed
;---------------------------------------------------------------------------

DSPMENUBOX
        MOVEM.L D0-D1,-(SP)     ;save regs
        BSET    #MENU,STATFLGS  ;set working with menu flag
        BSR     CLRMENU	        ;first clear the menu bar
        ADDQ    #2,D1	        ;bump length for bottom border
        MOVE    #MENUSTRT,A1    ;set menu starting point
        BSR     MAKEBOX	        ;display the box
        MOVEA   #MENULOC,A1     ;set up menu heading display point
        BSR     GETROWCOL       ;convert to screen ptrs

        CLR.L   D1	        ;don't display '...' string
        BSR     DSPSTRING       ;display the title
        SUBA.L  A2,A3	        ;get length of menu title
        MOVE.L  A3,D2	        ;move to working reg
        ADDQ    #2,D2	        ;add extra bytes to cover entire title
        ANDI.B  #$FE,D2	        ;ensure it's even
        MOVE    D2,D0	        ;save as width of menu heading "box"
        MOVEQ   #14,D1	        ;set height for "box"
        MOVEQ   #-1,D2	        ;set fill pattern
        SUB.W   #91,A1	        ;decrement start pt by 1 row + 1 byte
        BSR     INVERSE	        ;go hilite it
        MOVEM.L (SP)+,D0-D1     ;restore regs
        RTS

        .ENDC		        ;{MENU}

;---------------------------------------------------------------------------
;  Subroutine to create Service mode window
;---------------------------------------------------------------------------

MAKESVCW
        MOVEA   #SVCSTRT,A1     ;left corner point		        RM000
        MOVEQ   #SVCWIDTH,D0    ;width of window
        MOVE.L  #SVCHIGH,D1     ;height
        LEA     SVCMSG,A3       ;title
        BSR     MAKEWINDOW      ;go do it
        MOVE    #FIRSTROW,CRTROW ;init screen ptrs
        MOVE    #FIRSTCOL,CRTCOL
        RTS

        .ENDC		        ;{USERINT}

;---------------------------------------------------------------------------
;  Do display memory operation
;---------------------------------------------------------------------------

DSPMEM  BSR     GETA	        ;go get address
        BCS     INVALID
        TST     D3	        ;if no input go back to menu line

        .IF  USERINT = 0
        BEQ.S   DSPMENU
        .ELSE
        BEQ     LEV2LOOP
        .ENDC

        BCLR    #0,D2	        ;ensure even address
        MOVE.L  D2,A2	        ;and save

;  Check for all input on one line

        BSR     GETCH	        ;read queue to see if more input
        BCS.S   @1	        ;skip if not
        CMPI.B  #' ',D0	        ;must be a space separator
        BEQ.S   RDCNT	        ;skip if yes

@1      LEA     CNTMSG,A3       ;display count prompt
        BSR     PROMPT

;  Decode count input and do display

RDCNT   MOVEQ   #4,D1	        ;go get count (max of $FFFF)
        BSR     GETPARM
        BCS     INVALID
        BSR     PUTLF	        ;set display ptrs and space 1 line
        TST     D3	        ;set default count if no input
        BEQ.S   @4
        BRA.S   @5

@4      MOVEQ   #16,D2	        ;set default count

;  Do display of memory

@5      MOVE.L  A2,D0	        ;get display address
        MOVEQ   #8,D1	        ;and display it
        BSR     OUTCH

        .IF  USERINT = 0
        MOVEQ   #15,D6	        ;set col ptr for data display
        .ELSE
        ADDQ    #4,D6	        ;bump col for data display
        .ENDC

        MOVEQ   #8,D4	        ;set loop count
@6      MOVE    (A2)+,D0        ;read data word
        MOVEQ   #4,D1	        ;display it
        BSR     OUTCH
        ADDQ    #1,D6	        ;add space
        SUBQ    #1,D4	        ;loop for one line
        BNE.S   @6

        SUBQ.L  #8,D2	        ;decr data count		        RM000
        SUBQ.L  #8,D2	        ;				        RM000
        BLE.S   @7	        ;exit if done
        BSR     PUTLF	        ;go to next line
        BRA.S   @5	        ;and continue until done

@7      BSR     PUTLF	        ;add blank line

        .IF  USERINT = 0
        BRA     DSPMENU	        ;go wait for next command
        .ELSE
        BRA     LEV2LOOP        ;continue level2 loop
        .ENDC

        .PAGE
;---------------------------------------------------------------------------
;  Do set memory operation - enables setting of bytes, words or longs
;  up to 24 bytes max.  Decodes data length to determine type of operation.
;---------------------------------------------------------------------------

SETMEM  BSR     GETA	        ;go get address
        BCS     INVALID	        ;abort if invalid
        TST     D3	        ;any input?

        .IF  USERINT = 0
        BEQ     DSPMENU	        ;abort if none
        .ELSE
        BEQ     LEV2LOOP        ;abort if none
        .ENDC

        MOVE.L  D2,A2	        ;save target address

;  Check for all input on one line

        BSR     GETCH	        ;read queue to see if more input
        BCS.S   @1	        ;skip if not
        CMPI.B  #' ',D0	        ;must be a space separator
        BEQ.S   RDDTA	        ;skip if yes

@1      LEA     DATAMSG,A3      ;else output data prompt
        BSR     PROMPT

;  Decode parameter input and do operation

RDDTA   MOVEQ   #8,D1	        ;get max of 8 chars
        BSR     GETPARM
        BCS     INVALID
        TST     D3	        ;any input?

        .IF  USERINT = 0
        BEQ     DSPMENU	        ;abort if none
        .ELSE
        BEQ     LEV2LOOP
        .ENDC

;  write data to memory

        CMP.B   #2,D3	        ;first test for byte operation
        BGT.S   @1
        MOVE.B  D2,(A2)+        ;write byte
        BRA.S   @3

@1      MOVE.L  A2,D0	        ;ensure even address for word or long op
        BCLR    #0,D0
        MOVE.L  D0,A2
        CMP.B   #4,D3	        ;test for word op
        BGT.S   @2
        MOVE    D2,(A2)+        ;write word
        BRA.S   @3

@2      MOVE.L  D2,(A2)+        ;write long

@3      BSR     GETCH	        ;read input queue
        BCS.S   @4	        ;skip if none
        CMPI.B  #' ',D0	        ;must be a space separator
        BNE     INVALID	        ;exit if error
        BRA.S   RDDTA	        ;else continue operation

@4
        .IF  USERINT = 0
        BRA     DSPMENU	        ;go wait for next command
        .ELSE
        BRA     LEV2LOOP        ;continue level2 loop
        .ENDC

        .PAGE
;---------------------------------------------------------------------------
;  Do 'call' function - ensures address is on word boundary.
;---------------------------------------------------------------------------

CALLRTN BSR     GETA	        ;go get address
        BCS     INVALID
        TST     D3	        ;abort if no input

        .IF  USERINT = 0
        BEQ     DSPMENU	        ;go wait for next command
        .ELSE
        BEQ     LEV2LOOP        ;continue level2 loop
        .ENDC

        BCLR    #0,D2	        ;else ensure on word boundary
        MOVE.L  D2,A6SAV        ;save for jump

;  load registers from save area before jumping

        LEA     DATARGS,A6      ;get ptr
        MOVEM.L (A6)+,D0-D7/A0-A5  ;load regs
        MOVE.L  A6SAV,A6        ;restore address
        JSR     (A6)	        ;and do call
        BSR     SAVEREGS        ;save registers on exit

        .IF  USERINT = 0
        BRA     DSPMENU	        ;go wait for next command
        .ELSE
        BRA     LEV2LOOP        ;continue level2 loop
        .ENDC

        .IF     ROM16K = 1
        .PAGE
;---------------------------------------------------------------------------
;  Do loop on diagnostic
;---------------------------------------------------------------------------

LOOPTST
        .IF USERINT = 1
        BSR     MAKESVCW        ;redraw service window
        BSR     PUTLF	        ;add blank line and setup ptrs
        LEA     TSTMENU,A3      ;set ptr to test choices
        MOVEQ   #FIRSTCOL,D4    ;set left margin
        BSR     DSPMSG	        ;and go do display choices
        .ENDC

        LEA     TSTMSG,A3       ;display test routine prompt
        BSR     PROMPT
        BCS     INVALID	        ;skip if bad input
        TST     D3	        ;any input?

        .IF  USERINT = 0
        BEQ     DSPMENU	        ;abort if none
        .ELSE
        BNE.S   @0	        ;skip if yes
        BSR     MAKESVCW        ;else redraw service window
        BRA     LEV2LOOP        ;and return to level 2
        .ENDC

@0      SUBQ    #1,D3	        ;ensure only one char input
        BNE     INVALID	        ;skip if more than one

        MOVEQ   #1,D1	        ;go get one character of input
        BSR     GETPARM
        CMP.B   #MAXTEST,D2     ;check if within max range
        BHI     INVALID	        ;exit if not
        SUBQ    #1,D2	        ;decr for index
        BMI     INVALID	        ;skip if negative (i.e., 0 was input)

;----------------------------------------------------------------------------
;  Program NMI key to exit loop
;
;	 LEA	 LOOPEND,A3	 ;set NMI vector
;	 MOVE.L	 A3,NMIVCT
;	 MOVEQ	 #$5A,D0	 ;set / key for NMI
;	 BSR	 COPSCMD
;	 MOVEQ	 #$61,D0
;	 BSR	 COPSCMD
;----------------------------------------------------------------------------

        .IF  USERINT = 1

        .IF  FULLSCC = 0
        CMP.B   #7,D2	        ;SCC test selected?
        BEQ.S   NOSCCTST        ;skip if yes
        .ENDC

        MOVE.L  D2,-(SP)        ;save test #
        BSR     CLRDESK	        ;clear desktop except for menu bar
        BSR     MAKETEST        ;draw test window
        MOVE.L  (SP)+,D2        ;restore test #
        CMP.B   #4,D2	        ;CPU test?
        BGE.S   @1	        ;skip if not
        MOVEA   #CPUSTRT,A1     ;else set ptr for CPU board icon	        RM000
        BRA.S   @4	        ;and go hilite it

@1      CMP.B   #10,D2	        ;I/O board test?
        BGE.S   @2
        MOVEA   #IOSTRT,A1      ;set ptr for I/O board icon		        RM000
        BRA.S   @4

@2      CMP.B   #11,D2	        ;memory test?
        BGE.S   @3
        MOVEA   #MEMSTRT,A1     ;set ptr for memory board icon		        RM000
        BRA.S   @4

@3      MOVEA   #XCRDSTRT,A1    ;else must be I/O slot card		        RM000

@4      BSR     INVICON	        ;display in test window
        .ENDC

        ADD     D2,D2	        ;double test # for table index
        BSET    #LOOP,D7        ;set loop flag
        LEA     LOOPTBL,A0      ;and jump to requested routine
        ADD     0(A0,D2.W),A0
        JMP     (A0)

;---------------------------------------------------------------------------
;  Loop exit via NMI routine
;
;LOOPEND BTST	 #1,STATREG	 ;parity error?
;	 BEQ	 NMI		 ;skip if yes to report error
;	 MOVE.L	 #STKBASE,SP	 ;else restore stack
;	 BRA	 LEVEL2		 ;and redisplay service mode
;---------------------------------------------------------------------------

;  special entry points for routines that require initial setup

MMUTSTE1
        TST.B   SETUPON	        ;turn on SETUP bit for MMU tests
        BRA     MMUTST	        ;go do main test

        .IF  FULLSCC = 0
NOSCCTST		        ;SCC test not available
        BSR     CLRDESK	        ;clear desktop
        BSR     MAKESVCW        ;redraw service window
        BRA     NOTAVAIL        ;and exit
        .ENDC

MEMTST3 BSET    #6,MEMCODE      ;set for extended memory test
        MOVEQ   #PROFILE,D0     ;and for normal boot default (Profile)
        BSR     SAV2PM	        ;save in parameter memory
        BRA     MEMLOOP	        ;and go do memory testing

;  jump table for looping on start-up diagnostics

LOOPTBL .WORD   ROMTST-LOOPTBL	        ;1 = ROM checksum test
        .WORD   MMUTSTE1-LOOPTBL        ;2 = MMU test
        .WORD   VIDCHK-LOOPTBL	        ;3 = Video test
        .WORD   PARTST-LOOPTBL	        ;4 = Parity logic test
        .WORD   VIA2TST-LOOPTBL	        ;5 = Parallel port VIA test
        .WORD   VIA1CHK-LOOPTBL	        ;6 = Keyboard port VIA test
        .WORD   COPSENBL-LOOPTBL        ;7 = I/O board COPS test

        .IF  FULLSCC = 1
        .WORD   SCCTEST-LOOPTBL	        ;8 = SCC test
        .ELSE
        .WORD   NOSCCTST-LOOPTBL        ;SCC available only with final LISA's
        .ENDC

        .WORD   DSKTST-LOOPTBL	        ;9 = disk controller test
        .WORD   CLKTST-LOOPTBL	        ;A = clock test
        .WORD   MEMTST3-LOOPTBL	        ;B = memory test
        .WORD   CONFIG2-LOOPTBL	        ;C = configuration check

        .ENDC
        .PAGE
;---------------------------------------------------------------------------
;  Display video adjust pattern
;---------------------------------------------------------------------------

VIDAJST MOVEQ   #-1,D0	        ;first erase the screen
        BSR     WRTSCRN

;  Next draw the horizontal lines

        CLR.L   D0	        ;set scan line
        BSR.S   DRWHORZ	        ;go draw white line
        MOVEQ   #27,D0	        ;set next scan line
        MOVEQ   #28,D1	        ;set increment value also
        MOVEQ   #12,D2	        ;set line count
@1      BSR.S   DRWHORZ	        ;draw some more
        ADD     D1,D0	        ;incr to next line position
        DBF     D2,@1	        ;loop until done

;  Now draw the vertical lines

        CLR.L   D0	        ;set pixel #
        BSR.S   DRWVERT	        ;draw a vertical line
        MOVEQ   #44,D0	        ;set next pixel position
        MOVEQ   #45,D1	        ;set incr value also
        MOVEQ   #15,D2	        ;set line count
@2      BSR.S   DRWVERT	        ;draw some more
        ADD     D1,D0	        ;incr to next pixel position
        DBF     D2,@2	        ;loop until done

;  Wait for any keystroke to terminate display

        BSR     READKEY
        BRA     LEVEL2	        ;return to menu display

        .PAGE
;----------------------------------------------------------------------------
;  Subroutine to draw horizontal lines.	 Requires inputs:
;    D0 = scan line (0 to 363 decimal)
;    $110 = base address of screen
;----------------------------------------------------------------------------

DRWHORZ MOVEM.L D0/D1/A0,-(SP)  ;save regs
        MOVEQ   #90,D1	        ;line length in bytes
        MULU    D1,D0	        ;compute address offset
        MOVE.L  SCRNBASE,A0     ;get base screen address
        ADDA.L  D0,A0	        ;add offset
@1      CLR.B   (A0)+	        ;draw the line
        SUBQ    #1,D1
        BNE.S   @1
        MOVEM.L (SP)+,D0/D1/A0  ;restore
        RTS

;----------------------------------------------------------------------------
;  Subroutine to draw vertical lines.  Requires inputs:
;    D0 = pixel position (0 to 719 decimal)
;    $110 = base address of screen
;----------------------------------------------------------------------------

DRWVERT MOVEM.L D0-D3/A0,-(SP)  ;save regs
        MOVEQ   #8,D1	        ;pixels per byte
        DIVU    D1,D0	        ;compute address offset
        CLR.L   D2
        MOVE    D0,D2	        ;save offset
        MOVE.L  SCRNBASE,A0     ;get base screen address
        ADDA.L  D2,A0	        ;add offset

        SWAP    D0	        ;get remainder
        SUB     D0,D1	        ;compute bit position to set
        SUBQ    #1,D1

        MOVEQ   #90,D2	        ;distance to next pixel
        MOVE    #364,D3	        ;line length
@1      BCLR    D1,(A0)	        ;draw the line
        ADDA.L  D2,A0	        ;compute next pixel to set
        SUBQ    #1,D3
        BNE.S   @1	        ;loop until done
        MOVEM.L (SP)+,D0-D3/A0  ;restore
        RTS

        .PAGE
        .IF  BURNIN = 1
;-----------------------------------------------------------------------------
;  Power cycle entry point - branches to cycling routine
;-----------------------------------------------------------------------------

PowerCycle
        BSR     CLRDESK	        ;clear desktop
        BRA     CHKPAS2	        ;go start power cycle

        .ENDC

        .PAGE
;----------------------------------------------------------------------------
;  Invalid input detected - beep speaker and notify user
;----------------------------------------------------------------------------

INVALID BSR     SQUAWK	        ;that's a no no

        .IF  USERINT = 0
        LEA     WHATMSG,A3
        BSR     WRTBOX1
        BRA     DSPMENU	        ;let's try again
        .ELSE
        LEA     WHATMSG,A3      ;output question mark
        BSR     DBOXDSPLY       ;display in dialog box
        BRA.S   INVXIT

LEV2LOOP
        BSR     CLRDBOX	        ;go remove dialog box
INVXIT
        BSR     WRTMENU	        ;redisplay pull-down menu
        BSR     CursorDisplay   ;redisplay cursor
        BRA     GETLEV2	        ;and go get more input
        .ENDC

        .IF  ROM16K = 1
        .IF  FULLSCC = 0
;  Routine for not available message

NOTAVAIL
        BSR     SQUAWK	        ;alert user with a beep
        LEA     NOTAMSG,A3      ;set ptr for message
        BSR     DBOXDSPLY       ;display in dialog box
        BRA.S   INVXIT	        ;and go return for more input

        .ENDC		        ;{FULLSCC}
        .ENDC		        ;{ROM16K}

        .PAGE
        .IF  USERINT = 0
;-------------------------------------------------------------------------
;       Cursor Routines for Service mode
;-------------------------------------------------------------------------

;  Routine to display rectangular cursor

SETCUR  BSR     SETCRSR	        ; set A6 with cursor location
        CLR.B   (A6)	        ; set white first
        CLR.B   R1(A6)
        CLR.B   R2(A6)
        CLR.B   R3(A6)
        CLR.B   R4(A6)
        CLR.B   R5(A6)
        CLR.B   R6(A6)
        CLR.B   R7(A6)

;  Entry point to erase cursor

CLRCUR  BSR     SETCRSR	        ; set A6 with cursor location
        NOT.B   (A6)	        ;  then complement it
        NOT.B   R1(A6)
        NOT.B   R2(A6)
        NOT.B   R3(A6)
        NOT.B   R4(A6)
        NOT.B   R5(A6)
        NOT.B   R6(A6)
        NOT.B   R7(A6)
        RTS		        ; and thats all there is...

;--------------------------------------------------------------------------
;  SCROLL  - move contents of screen up one whole line except for top 3
;  lines which are saved for menu display.
;  We assume that we are at bottom line when called. D6 (column) will be
;  left alone, but D5 (row) will be set at 32.
;--------------------------------------------------------------------------

SCROLL  MOVEM.L A0/D6,-(SP)     ;save current column and bfr ptr
        MOVEQ   #FIRSTROW,D5    ;and set to beginning row
        CLR.L   D6
        BSR     SETCRSR	        ;get address of screen
        MOVE.L  A6,A0	        ;set as from ptr
        ADDA    #RBYTES,A0
        MOVE    #30*RLONGS,D1   ;set loop for long copies
@1      MOVE.L  (A0)+,(A6)+     ;scroll it
        SUBQ    #1,D1
        BGT.S   @1
        MOVEQ   #LASTROW,D5     ;peg at bottom
        MOVEM.L (SP)+,A0/D6     ;restore old column and bfr ptr
        RTS

        .PAGE

;  PUTLF - advance to next row; this may cause a scroll if at bottom

PUTLF   MOVE.B  CRTROW,D5       ;get last state
        MOVEQ   #FIRSTCOL,D6    ;update
        ADDQ    #1,D5
        CMPI    #LASTROW,D5
        BLE.S   @9	        ;skip if its ok
        BSR.S   SCROLL	        ; else, do a scroll operation
@9      MOVE.B  D5,CRTROW       ;save updates
        MOVE.B  D6,CRTCOL
        RTS

;  PUTBS - move cursor left one position.

PUTBS   SUBQ.B  #1,D6
        CMP.B   #10,D6	        ;stop at 10th col
        BGE.S   @9
        MOVEQ   #10,D6
@9      RTS

;-----------------------------------------------------------------------------
;  Subroutine to output prompt line and gather input
;-----------------------------------------------------------------------------

PROMPT  MOVEM.L D5-D6/A2,-(SP)  ;save screen ptrs and target address
        MOVEQ   #1,D5
        CLR.L   D6
        BSR     SETCRSR	        ;get address in A6
        MOVE    #RLONGS,D0
@1      CLR.L   (A6)+	        ;clear box
        SUBQ    #1,D0
        BNE.S   @1
        MOVEQ   #1,D6	        ;add space
        BSR     DSPMSG	        ;write msg
        BSR     SETCUR
        BSR     DRWLINE
        BSR     RDINPUT	        ;go handle input
        MOVEM.L (SP)+,D5-D6/A2  ;restore and exit
        RTS

;-----------------------------------------------------------------------------
;  Subroutine to read keyboard input and save in buffer.  Accepts max of
;  64 characters.
;-----------------------------------------------------------------------------

RDINPUT
        MOVEA   #KBDBFR,A0      ;set buffer ptrs		        RM000
        MOVE.L  A0,A1	        ;same for head and tail
        CLR.L   D3	        ;clear for result use

READIN  BSR     READKEY	        ;get char
        BSR     KeyToASCII      ;convert to ASCII

        TST.B   D0	        ;ignore CMD, Option, Shift, Alpha lock
        BEQ.S   READIN

        .LIST
        CMP.B   #BS,D0	        ;backspace key?
        BNE.S   @2
        TST     D3	        ;any input
        BEQ.S   READIN	        ;no - ignore
        SUBQ    #1,D3	        ;decrement count
        TST.B   -(A1)	        ;delete char from queue
        BSR     CLRCUR
        BSR     PUTBS	        ;do backspace on screen
        BSR     SETCUR
        BRA.S   READIN	        ;and continue

@2      CMP.B   #RET,D0	        ;return key?
        BEQ.S   @3	        ; yes - exit

        CMP.B   #64,D3	        ;at max?
        BLT.S   @4	        ;skip if no
        BSR     SQUAWK	        ;else notify user
        BRA.S   READIN	        ;and ignore input

@4      ADDQ    #1,D3	        ;incr char count
        BSR     ENQKBD	        ;queue it
        BSR     DSPVAL	        ;and output it
        BSR     SETCUR	        ;advance cursor
        BRA.S   READIN	        ;and continue read

@3      BSR     CLRCUR	        ;erase cursor to signify input accepted
        RTS

        .ELSE
;-----------------------------------------------------------------------------
;  Subroutine to output prompt line and gather input
;-----------------------------------------------------------------------------

PROMPT  MOVEM.L D5-D6/A2,-(SP)  ;save screen ptrs and target address
        BSR     MAKEDBOX        ;make dialog box
        MOVE    #DBOXROW,D5     ;set msg ptrs
        MOVE    #DBOXCOL,D6
        BSR     GETLENGTH       ;get message length
        ADDQ    #2,D2	        ;incr for spacing
        MOVE    D2,MSGLEN       ;save as message length
        BSR     DSPMSG	        ;write msg
        BSR     RDINPUT	        ;go handle input
        MOVEM.L (SP)+,D5-D6/A2  ;restore and exit
        RTS

;-----------------------------------------------------------------------------
;  Subroutine to read keyboard input and save in buffer.  Accepts max of
;  48 characters.
;-----------------------------------------------------------------------------

RDINPUT
        MOVE.L  #KBDBFR,A0      ;set buffer ptrs
        MOVE.L  A0,A1	        ;same for head and tail
        CLR.L   D3	        ;clear for result use

READIN  BSR     READKEY	        ;get char
        BSR     KeyToASCII      ;convert to ASCII

        TST.B   D0	        ;ignore CMD, Option, Shift, Alpha lock
        BEQ.S   READIN

        CMP.B   #BS,D0	        ;backspace key?
        BNE.S   @2
        TST     D3	        ;any input
        BEQ.S   READIN	        ;no - ignore
        SUBQ    #1,D3	        ;decrement count
        TST.B   -(A1)	        ;delete char from queue
        BSR     PUTBS	        ;do backspace on screen
        BSR     CLRIT	        ;clear char on screen
        BRA.S   READIN	        ;and continue

@2      CMP.B   #RET,D0	        ;return key?
        BEQ.S   @3	        ; yes - exit

        CMP.B   #48,D3	        ;at max?
        BLT.S   @4	        ;skip if no
        BSR     SQUAWK	        ;else notify user
        BRA.S   READIN	        ;and ignore input

@4      ADDQ    #1,D3	        ;incr char count
        BSR     ENQKBD	        ;queue it
        BSR     DSPVAL	        ;and output it
        BRA.S   READIN	        ;and continue read

@3      RTS

;--------------------------------------------------------------------------
;  SCROLL  - move contents of Service Window up one whole line.	 Assumed
;  that we are at bottom line when called. D6 (column) and D5 (row) are
;  set to start of last line.
;--------------------------------------------------------------------------

SCROLL  MOVEM.L D0-D2/A0,-(SP)	        ;save data regs and bfr ptr
        MOVEQ   #FIRSTROW+ROWLINES,D5   ;set beginning character row +1
        MOVEQ   #FIRSTCOL,D6	        ; and beginning column
        BSR     SETCRSR		        ;get address of screen
        MOVE.L  A6,A0		        ;set as to ptr
        ADDA    #<RBYTES*10>,A0	        ;set from ptr down one character row	        RM000
        MOVE    #NROWS,D2	        ;number of rows to move

@1      MOVE    #ROWLINES,D1    ;number of pixel lines per character row
@2      MOVE    #ROWLEN,D0      ;length of a pixel line in window
@3      MOVE    (A0)+,(A6)+     ;scroll it
        SUBQ    #2,D0	        ;do entire pixel line
        BGT.S   @3

        ADDQ    #1,D5	        ;bump to next row
@4      MOVEQ   #FIRSTCOL,D6    ;set first col
        BSR     SETCRSR	        ;compute address
        MOVE.L  A6,A0	        ;set as to ptr
        ADDA    #<RBYTES*10>,A0 ;set from ptr down one character row		        RM000
        SUBQ    #1,D1	        ;do all pixel lines
        BNE.S   @2

        SUBQ    #1,D2	        ;finished a character row
        BNE.S   @1	        ; loop until done

        MOVE    #LASTROW,D5     ;peg at bottom
        MOVE    #FIRSTCOL,D6
        MOVEM.L (SP)+,D0-D2/A0  ;restore data regs and bfr ptr
        RTS

        .PAGE

;  PUTLF - advance to next row; this may cause a scroll if at bottom

PUTLF   MOVE    CRTROW,D5       ;get last state
        MOVEQ   #FIRSTCOL,D6    ;update column to left edge of window
        ADD     #ROWLINES,D5    ;bump row by number of pixel lines per row
        CMPI    #LASTROW,D5
        BLE.S   @9	        ;skip if its ok
        BSR.S   SCROLL	        ; else, do a scroll operation
@9      MOVE    D5,CRTROW       ;save updates
        MOVE    D6,CRTCOL
        RTS

;  PUTBS - move cursor left one position.
;  Assumes location MSGLEN = left most column for window.

PUTBS   SUBQ    #1,D6
        CMP     MSGLEN,D6	 ;stop at left edge
        BGE.S   @9
        MOVE    MSGLEN,D6
@9      RTS

;  Routine to erase data on screen

CLRIT   MOVE    #' ',D0	        ; output a space
        BSR     DSPVAL
        SUBQ    #1,D6	        ; reposition col ptr
        RTS		        ; and that's all there is...

        .ENDC
        .PAGE
;-----------------------------------------------------------------------------
;  Subroutine to save keyboard input in buffer - ignores data if buffer full.
;-----------------------------------------------------------------------------

ENQKBD  MOVE.L  A2,-(SP)        ;save working reg
        MOVEA   #KBDEND,A2      ;get ptr to end of buffer	        RM000
        CMPA.L  A1,A2	        ; at end of buffer?
        BEQ.S   @9	        ; exit if yes
        MOVE.B  D0,(A1)+        ; else do save
@9      MOVE.L  (SP)+,A2        ;restore
        RTS

;-----------------------------------------------------------------------------
;  This code gets the next byte from the keyboard queue and delivers it to
;  caller in D0.
;-----------------------------------------------------------------------------

GETCH   CMPA.L  A0,A1	        ;check if any data
        BEQ.S   @1	        ;exit if none
        MOVE.B  (A0)+,D0        ;get data
        BRA.S   @2
@1      ORI.B   #$01,CCR        ;set empty indicator
@2      RTS

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to get address parameter
;-------------------------------------------------------------------------

GETA    LEA     ADDRMSG,A3      ;output prompt and get input
        BSR     PROMPT
        MOVEQ   #8,D1	        ;decode address (max of 8 digits)
        BSR.S   GETPARM
        RTS

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to get input parameters.  Reads Ascii values from keyboard
;  buffer and calls conversion routine to return hex values.  Stops after
;  reading requested input or 'space' separator encountered.
;  Inputs:  D1 = max chars to read
;  Outputs: D2 = value read
;	    D3 = # of chars read
;  Carry bit set if invalid chars.
;-------------------------------------------------------------------------

GETPARM CLR.L   D3	        ;use for counter
        CLR.L   D2	        ;use for result

READQ   BSR     GETCH	        ;check input queue
        BCS.S   GETEXIT	        ;exit if no chars
        CMPI.B  #' ',D0	        ;space separator?
        BNE.S   @3	        ;if not, go response
        TST.B   -(A0)	        ;replace on queue
        BRA.S   GETEXIT	        ;and exit

@3      CMPI.B  #'0',D0	        ;check if valid hex char
        BLT.S   INVPARM
        CMPI.B  #'9',D0
        BLS.S   OKCH	        ;OK if 0-9
        CMPI.B  #'A',D0	        ; or A-F
        BLT.S   INVPARM
        CMPI.B  #'F',D0
        BGT.S   INVPARM

OKCH    BSR.S   CONVERT	        ;convert to hex digit
        LSL.L   #4,D2	        ;save char
        OR.B    D0,D2
        ADDQ    #1,D3	        ;bump counter
        CMP.B   D3,D1	        ;at max?
        BNE.S   READQ	        ;continue if no

GETEXIT ANDI.B  #$FE,CCR        ;clear error indicator
        BRA.S   GETXIT2	        ;and exit

INVPARM ORI.B   #$01,CCR        ;set error indicator

GETXIT2 RTS

;-------------------------------------------------------------------------
;  Subroutine to convert Ascii character to hex.  Expects input in D0
;  and returns converted value in D0.
;-------------------------------------------------------------------------

CONVERT CMP.B   #$40,D0	        ;check if number or letter
        BGT.S   @1	        ;skip if letter
        SUBI.B  #$30,D0	        ;simple operation for number
        BRA.S   @9

@1      SUBI.B  #$41,D0	        ;a little different for letters
        ADDI.B  #$0A,D0

@9      RTS

        .PAGE
        .IF  USERINT = 1
;-------------------------------------------------------------------------
;  Subroutine to write to dialog box
;-------------------------------------------------------------------------

DBOXDSPLY
        MOVEM.L D0/D5-D6,-(SP)  ;save D0 and current cursor ptrs
        BSR     MAKEDBOX        ;clear dialog box and redraw
        MOVE    #DBOXROW,D5     ;set box coordinates
        MOVE    #DBOXCOL,D6
        BSR     SETCRSR	        ;get address in A6
        BSR     DSPMSG	        ;write msg
        MOVEM.L (SP)+,D0/D5-D6  ;restore
        RTS		        ;and exit

;-------------------------------------------------------------------------
;  Subroutine to remove dialog box from screen
;-------------------------------------------------------------------------

CLRDBOX MOVEQ   #ROWBYTES,D0	        ;set pixel line length			        RM000
        MOVEA   #DESKLINE,A6	        ;set starting point as bottom of menu line      RM000
        CLR.L   D1
        MOVE.L  A6,A5		        ;set limit as bottom of dialog box
        MOVE.L  #<DBOXHIGH*ROWBYTES>,D1 ; by adding box heigth to start pt
        ADD.L   #DBOXTOP,D1
        ADDA.L  D1,A5
        BSR     GRAY		        ;redraw gray pattern
        RTS

;-----------------------------------------------------------------------------
;  GETINPUT routine - waits for inputs from mouse or keyboard and returns
;  with keycode in D0 if keyboard input, or rectangle ID if active rect is
;  selected with the mouse.  If CMD flag is set, keyboard input returned
;  only when prefaced by the CMD key.
;-----------------------------------------------------------------------------

;  State 1 - General wait

GETINPUT
        BTST    #CMDFLG,STATFLGS ;command key still down?
        BNE.S   GET2		 ;skip if yes

GET1    BSR     WT4INPUT        ;else go wait for COPS input

CHKIT
        CMP.B   #MOUSUP,D0      ;mouse button up?
        BNE.S   @1
        BCLR    #MOUSE,STATFLGS ;clear mouse flag
        BRA.S   GET1	        ;and go wait for more input

@1      CMP.B   #MOUSDWN,D0     ;mouse button down?
        BNE.S   @2
        BSET    #MOUSE,STATFLGS ;set reminder flag
        BRA.S   @3

@2      TST.B   D0	        ;mouse data?
        BNE.S   @5	        ;skip if not
        BSR     CursorHide      ;else clear old cursor
        BSR     CursorDisplay   ;and redisplay cursor in new position

        BTST    #MOUSE,STATFLGS ;is mouse button down?
        BEQ.S   GET1	        ;skip if not

@3      BSR     CHKPOSN	        ;else go check mouse position
        BEQ.S   GET1	        ;continue if not over a rect
        BRA     GET3	        ;else go to state 3

@5      CMP.B   #CmdDwn,D0      ;command key down?
        BNE.S   @6
        BSET    #CMDFLG,STATFLGS ;set flag if yes
        BRA.S   GET2		 ;and go to state 2

@6      CMP.B   #CmdUp,D0	 ;command key up?
        BNE.S   @4
        BCLR    #CMDFLG,STATFLGS ;clear flag if yes
        BRA.S   GET1		 ;and continue in loop

@4      BTST    #CHKCMD,STATFLGS ;CMD key prefix required?
        BNE.S   GET1		 ;loop if yes

@7      TST.B   D0	        ;else check if downstroke
        BPL.S   GETINPUT        ;skip upstrokes
        BSR     CHKINPUT        ;go check if rectangle selected
        RTS		        ;and return with keycode


;  State 2 - Command (apple) button down

GET2

WAIT2   BSR     WT4INPUT        ;else go wait for COPS input

CHKIT2  CMP.B   #CMDUP,D0       ;command key up?
        BNE.S   @2	        ;skip if not
        BCLR    #CMDFLG,STATFLGS ;else clear flag
        BRA.S   GET1	        ;and return to state 1

@2      CMP.B   #MOUSUP,D0      ;mouse button up?
        BNE.S   @3
        BCLR    #MOUSE,STATFLGS ;clear mouse flag
        BRA.S   GET2	        ;and go wait for more input

@3      CMP.B   #MOUSDWN,D0     ;mouse button down?
        BNE.S   @4
        BSET    #MOUSE,STATFLGS ;set reminder flag
        BRA.S   @5

@4      TST.B   D0	        ;mouse data?
        BNE.S   @6
        BSR     CursorHide      ;else clear old cursor
        BSR     CursorDisplay   ;and redisplay cursor in new position

        BTST    #MOUSE,STATFLGS ;is mouse button down?
        BEQ.S   GET2	        ;skip if not

@5      BSR     CHKPOSN	        ;else go check mouse position
        BEQ.S   GET2	        ;continue if not over a rect
        BRA.S   GET3	        ;else go to state 3

@6      BPL.S   WAIT2	        ;ignore upstrokes
        BSR     CHKINPUT        ;go check if rectangle selected
        RTS		        ;and return with keycode

;  State 3 - Mouse button down and over an active rectangle

GET3    MOVE    D0,D1	        ;save rectangle ID
WAIT3   BSR     WT4INPUT        ;go wait for input

        CMP.B   #MOUSUP,D0      ;mouse button up?
        BNE.S   @1	        ;skip if not
        BCLR    #MOUSE,STATFLGS ;clear indicator
        MOVE    D1,D0	        ;restore rectangle ID
        RTS		        ;and go analyze input

@1      TST.B   D0	        ;mouse data?
        BNE.S   WAIT3	        ;continue wait if not - ignore keyboard input

@2      BSR     CursorHide      ;move cursor to new position
        BSR     CursorDisplay
        BSR     CHKPOSN	        ;check if over a rect
        BNE.S   GET3	        ;stay in this state if yes
        BRA     GETINPUT        ;else return to state 1

;-------------------------------------------------------------------------
;  WT4INPUT
;
;       Routine to wait for input from COPS.  Returns with keycode in D0
;       or sets D0 = 0 if mouse data received.
;
;-------------------------------------------------------------------------

WT4INPUT

;  State 0 - general wait

COPS0   BSR     ReadCOPS        ;get input from COPS
        TST.B   D0	        ;mouse data?
        BEQ.S   COPS1	        ;go to state 1 if yes
        CMP.B   #RSTCODE,D0     ;reset code?
        BEQ.S   COPS2	        ;skip to state 2 if yes
        RTS		        ;else return with the keycode

;  State 1 - waiting for mouse data

COPS1   BSR.S   ReadCOPS        ;get COPS input
        MOVE.B  D0,MousDx       ;save mouse delta-x
        BSR.S   ReadCOPS        ;read and
        MOVE.B  D0,MousDy       ;save mouse delta-y
        BSR     MouseMovement   ;record the mouse movement
        CLR     D0	        ;set mouse flag
        RTS		        ;and exit

;  State 2 - waiting for reset code

COPS2   BSR.S   ReadCOPS        ;get COPS input
        CMP.B   #$DF,D0	        ;reset code <= $DF?
        BLS.S   @1	        ;branch if yes
        CMP.B   #$EF,D0	        ;reset code <= $EF?
        BLS.S   @2	        ;skip if yes
        CMP.B   #$FB,D0	        ;reset code <= $FB
        BLO.S   @3	        ;branch if < $FB
        BEQ.S   @4	        ;branch if = $FB
        CMP.B   #$FD,D0	        ;reset code <= $FD?
        BLS.S   @3	        ;branch if <= $FD
        BRA.S   @5	        ;branch if > $FD

;  $00 - $DF    Keyboard ID number - save and return to state 0

@1      MOVE.B  D0,KeyID        ;save new ID
        BRA.S   COPS0	        ;return to general wait

;  $E0 - $EF    Clock data - save first nibble, and go to state 4

@2      MOVE.B  D0,ClockBytes   ;save it
        BRA.S   COPS4	        ;and go get rest of data

;  $F0 - $FA    Reserved --- ignored
;  $FC - $FD    Clock timer interrupt, keyboard unplugged --- ignored

@3      BRA.S   COPS0	        ;go back to general wait

;  $FB	        Soft on/off button - go to power-off routine

@4      BRA.S   PowerOff        ;go shutdown the system

;  $FE - $FF    COPS failure codes - go to error routine

@5      CMP.B   #$FE,D0	        ;I/O board COPS?
        BNE.S   @6
        MOVEQ   #EIOCOP,D0      ;else set I/O COPS error code
        BRA.S   @7
@6      MOVEQ   #EKBDCOP,D0     ;set keyboard COPS error code
@7      ORI.B   #$01,CCR        ;set return error indicator
        RTS

;  State 4 - waiting for clock data; use timeout routine to guard against error

COPS4   MOVEM.L D1-D2/A0-A2,-(SP) ;save regs
        LEA     ClockBytes+1,A1	  ;set ptrs to save area
        LEA     ClockBytes+6,A2
        MOVEQ   #5,D1		  ;5 more bytes expected
@1      BSR     GETDATA		  ;get COPS data
        BCS.S   @2		  ;skip if any errors
        SUBQ    #1,D1		  ;loop until done
        BNE.S   @1

@2      MOVEM.L (SP)+,D1-D2/A0-A2 ;restore regs and
        BRA.S   COPS0		  ;go back to general wait

        .ENDC	        ;{USERINT}

;------------------------------------------------------------------------
;  ReadCOPS
;
;       Routine to get data from COPS. Returns with data in D0.
;
;------------------------------------------------------------------------

ReadCOPS
        MOVE.L  A0,-(SP)
        MOVE.L  #VIA1BASE,A0    ;get interface ptr
@1      MOVE.B  IFR1(A0),D0     ;poll for data
        BTST    #1,D0
        BEQ.S   @1	        ;loop until data received
        MOVE.B  ORA1(A0),D0     ;read
        MOVE.L  (SP)+,A0        ;and return
        RTS

        .IF  USERINT = 1
;-------------------------------------------------------------------------
;  PowerOff - routine to shutdown the system
;-------------------------------------------------------------------------

PowerOff
        BTST    #DISK,D7        ;disk controller error?		        CHG023
        BNE.S   @9	        ;skip if yes			        CHG023
        MOVE.L  #DISKMEM,A0     ;set ptr for shared memory
        BSR.S   ENBLDRVS        ;enable both drives
        MOVE.B  #DRV1,DRV(A0)   ;eject diskette in drive 1
        BSR     EJCTDSK
        MOVE.B  #DRV2,DRV(A0)   ;and drive 2
        BSR     EJCTDSK

        MOVE.B  #DIE,(A0)       ;get Twiggy controller out of memory
        BSR     CMDCHK	        ;wait until command taken

@9      BSR4    CONOFF	        ;turn off contrast		        CHG003
        MOVE.L  #ONESEC,D0      ;wait for it to happen		        CHG003
        BSR     DELAY	        ;				        CHG003
        MOVEQ   #$21,D0	        ;power off, timer off, clock on	        RM000
        BSR     COPSCMD	        ;go do it
        BSR     KBDDELAY        ;wait about 1.7 secs for power-off

        BSR     SQUAWK	        ;error if still on
        LEA     IOBRD,A2
        MOVEQ   #EIOCOP,D0      ;set error code
        BRA     TSTXIT	        ;display error and go back to level1 monitor

;------------------------------------------------------------------------
;  Subroutine to enable drives
;------------------------------------------------------------------------

ENBLDRVS
        MOVE.B  #$88,CMD(A0)    ;enable both drives
        MOVE.B  #ENBLINT,(A0)
        BSR     CMDCHK
        MOVE.L  #VIA1BASE,A3    ;and enable FDIR
        BCLR    #FDIR,DDRB1(A3)
        RTS

;------------------------------------------------------------------------
;  CHKPOSN
;
;  Routine to check mouse position versus active rectangle table.
;  If over a rectangle, inverts it and returns with its ID.
;  If not over a rectangle, ensures all rectangles not inverted, and
;  returns with D0 = 0.
;
;  Active rectangle table has following format:
;
;       Word 1 : number of entries in table
;       Word 2 : ID for first entry, MSB = 1 if inverted on screen
;       Next 4 words contain X,Y pixel coordinates for upper left
;	 and bottom right corners
;       Each successive entry follows same format, with 5 words per entry
;
;       Register usage:
;	        A0 = ptr to rectangle table
;	        D0 = # of entries in table
;	        D1 = ID for current entry
;	        D2 = X-coordinate for upper left
;	        D3 = Y-coordinate for upper left
;	        D4 = X-coordinate for bottom right
;	        D5 = Y-coordinate for bottom right
;	        D6 = X-coordinate for current cursor location
;	        D7 = Y-coordinate for current cursor location
;
;       On exit, D0 = ID code of rectangle cursor is over or
;		    = 0 if not over any rectangle
;
;-------------------------------------------------------------------------

CHKPOSN MOVEM.L D1-D7/A0,-(SP)
        MOVE    CrsrX,D6        ;get current cursor location
        MOVE    CrsrY,D7
        LEA     RectTable,A0    ;set ptr to table
        MOVE    (A0)+,D0        ;get count
        BEQ.S   CHKPXIT	        ;exit if 0
GETNTRY MOVEM   (A0)+,D1-D5     ;else get entry (5 words)

;  check if cursor over rectangle

        CMP     D2,D6	        ;CrsrX < upper left X?
        BLT.S   @1	        ;branch if cursor to left of rectangle
        CMP     D4,D6	        ;CrsrX > bottom right X?
        BGT.S   @1	        ;branch if cursor to right of rectangle
        CMP     D3,D7	        ;CrsrY < upper left Y?
        BLT.S   @1	        ;branch if cursor above rectangle
        CMP     D5,D7	        ;CrsrY > bottom right Y?
        BGT.S   @1	        ;branch if cursor below rectangle
        BRA.S   @3	        ;cursor over this entry - go invert it

;  not over this entry - check if inverted, then continue through table

@1      TST     D1	        ;entry inverted?
        BPL.S   @2	        ;skip if not
        BSR     INVERT	        ;else go reinvert
@2      SUBQ    #1,D0	        ;decrement entry count
        BNE.S   GETNTRY	        ;check next entry if not done
        BRA.S   CHKPXIT	        ;else exit with D0 = 0

;  over the rectangle - if not already, invert it and data saved

@3      TST     D1	        ;already inverted?
        BMI.S   @6	        ;exit if yes
        BSR     INVERT	        ;go invert rectangle

;  check if any other entries previously inverted

@4      SUBQ    #1,D0	        ;done?
        BEQ.S   @6	        ;skip if yes
        TST     (A0)+	        ;else check next entry
        BMI.S   @5	        ;skip if inverted
        ADDQ    #8,A0	        ;else bump to next entry
        BRA.S   @4	        ;and continue loop

@5      MOVEM   (A0)+,D2-D5     ;get coordinates
        BSR     INVERT	        ;and go reinvert and then exit
                                ;since at most one rect inverted

@6      MOVE    D1,D0	        ;set return code

CHKPXIT MOVEM.L (SP)+,D1-D7/A0
        RTS		        ;and exit

;------------------------------------------------------------------------
;  CHKINPUT
;
;  Routine to check keyboard input versus active rectangle table.
;  If rectangle selected, inverts it and returns.
;
;  Active rectangle table has following format:
;
;       Word 1 : number of entries in table
;       Word 2 : ID for first entry, MSB = 1 if inverted on screen
;       Next 4 words contain X,Y pixel coordinates for upper left
;	 and bottom right corners
;       Each successive entry follows same format, with 5 words per entry
;
;       Register usage:
;	        A0 = ptr to rectangle table
;	        D0 = keyboard input
;	        D1 = ID for current entry
;	        D2 = X-coordinate for upper left
;	        D3 = Y-coordinate for upper left
;	        D4 = X-coordinate for bottom right
;	        D5 = Y-coordinate for bottom right
;	        D6 = # of entries in table
;
;-------------------------------------------------------------------------

CHKINPUT
        MOVEM.L D1-D6/A0,-(SP)
        LEA     RectTable,A0    ;set ptr to table
        MOVE    (A0)+,D6        ;get count
RDENTRY MOVEM   (A0)+,D1-D5     ;get entry (5 words)
        CMP.B   D0,D1	        ;match with keyboard input?
        BEQ.S   @1	        ;skip if yes
        SUBQ    #1,D6	        ;else loop thru all entries
        BNE.S   RDENTRY
        BRA.S   @2	        ;skip to exit

@1      BSR.S   INVERT	        ;go invert rectangle

@2      MOVEM.L (SP)+,D1-D6/A0  ;restore regs
        RTS		        ;and return

;-----------------------------------------------------------------------------
;  INVERT
;
;  Routine to invert buttons, menu or icon.  Computes upper left address,
;  width and heigth of rectangle, then calls INVERSE and other routines
;  as needed.
;
;  Register inputs: D2 = upper left X-coordinate
;		    D3 = upper left Y-coordinate
;		    D4 = bottom right X-coordinate
;		    D5 = bottom right Y-coordinate
;		    A0 = ptr to start of next entry in rectangle table
;
;-----------------------------------------------------------------------------

INVERT  MOVEM.L D0-D4/A1,-(SP)

        BSR     CursorHide      ;remove cursor

        BCHG    #7,-10(A0)      ;flip the invert bit indicator for entry

        MOVE    D4,D0	        ;compute width
        SUB     D2,D0
        DIVU    #8,D0	        ;convert to bytes
        MOVE    D5,D1	        ;compute height in pixel rows
        SUB     D3,D1
        ADDQ    #1,D1	        ;bump by 1 to do bottom line also

        SUBA.L  A1,A1	        ;use for upper left address
        LSR     #3,D2	        ;divide pixel column by 8 for bytes
        ADD     D2,A1	        ;add to address
        MOVEQ   #MaxX/8,D4      ;bytes per row on screen
        MULU    D4,D3	        ; * pixel row
        ADD     D3,A1	        ;address of upper left corner

        BTST    #MENU,STATFLGS  ;doing menu item?
        BEQ.S   @0	        ;skip if not
        ADDA    #ROWBYTES,A1    ;else add 1 scan line to avoid menu	        RM000
                                ; bar line inversion
        SUBQ    #1,D1	        ;and decr length to avoid inverting bottom line
                                ; of menu box

@0      MOVEQ   #-1,D2	        ;set fill pattern
        MOVEM.L D0-D2/A1,-(SP)  ;save args
        BSR     INVERSE	        ;invert it

        MOVEM.L (SP)+,D0-D2/A1  ;restore args

        BTST    #BTN,STATFLGS   ;doing buttons?
        BEQ.S   @1	        ;skip if not
        BSR     DRAWBUTN        ;redraw button
        BRA.S   @9	        ;and exit

@1      BTST    #MENU,STATFLGS  ;doing menu?
        BEQ.S   @9	        ;skip if not
        BSR     DRAWSIDES       ;just redraw sides

@9      BSR     CursorDisplay   ;redisplay cursor

        MOVEM.L (SP)+,D0-D4/A1  ;restore regs
        RTS		        ;and return

        .PAGE
;-----------------------------------------------------------------------------
;
;   Hardware Interface for the Mouse
;
;   Written by Rick Meyers
;   (c) Apple Computer Incorporated, 1983
;
;   The routines below provide an assembly language interface to the mouse.
;   Input parameters are passed in registers, output parameters are returned
;   in registers.  Unless otherwise noted, all registers are preserved.
;
;   The Mouse
;
;   The mouse is a pointing device used to indicate screen locations.  Mouse
;   coordinates are located between pixels on the screen.  Therefore, the
;   X-coordinate can range from 0 to 720, and the Y-coordinate from 0 to 364.
;   The initial mouse location is 0,0.
;
;   Mouse Scaling
;
;   The relationship between physical mouse movements and logical mouse
;   movements is not necessary a fixed linear mapping.  Three alternatives
;   are available: 1) unscaled, 2) scaled for fine movement and 3) scaled
;   for coarse movement.
;
;   When mouse movement is unscaled, a horizontal mouse movement of x units
;   yields a change in the mouse X-coordinate of x pixels.  Similiarly, a
;   vertical movement of y units yields a change is the mouse Y-coordinate
;   of y pixels.  These rules apply independent of the speed of the mouse
;   movement.
;
;   When mouse movement is scaled, horizontal movements are magnified by 3/2
;   relative to vertical movements.  This is intended to compensate for the
;   2/3 aspect ratio of pixels on the screen.  When scaling is in effect, a
;   distinction is made between fine (small) movements and coarse (large)
;   movements.  Fine movements are slightly reduced, while coarse movements
;   are magnified. For scaled fine movements, a horizontal mouse movement of
;   x units yields a change in the X-coordinate of x pixels, but a vertical
;   movement of y units yields a change of (2/3)*y pixels.  For scaled coarse
;   movements, a horizontal movement a x units yields a change of (3/2)*x
;   pixels, while a vertical movements of y units yields a change of y pixels.
;
;   The distinction between fine movements and coarse movements is determined
;   by the sum of the x and y movements each time the mouse location is
;   updated.  If this sum is at or below the 'threshold', the movement is
;   considered to be a fine movement.  Values of the threshold range from 0
;   (which yields all coarse movements) to 256 (which yields all fine
;   movements).	 Given the default mouse updating frequency, a threshold of
;   about 8 (threshold's initial setting) gives a comfortable transition between
;   fine and coarse movements.
;---------------------------------------------------------------------------------


;-----------------------------------------------------------------------------
;
;   Mouse Movement
;
;   This routine is called by the GETINPUT routine when the COPS has
;   reported mouse movement.  All registers are preserved.
;
;
;   Register Assignments:
;
;       D0  --  Mouse X-Coordinate (integer)
;       D1  --  Mouse Y-Corrdinate (integer)
;       D2  --  Mouse Dx (integer)
;       D3  --  Mouse Dy (integer)
;

MouseMovement   MOVEM.L D1-D5,-(SP)	    ; save registers
                MOVE.W  MousX,D0	    ; mouse X-coordinate
                MOVE.W  MousY,D1	    ; mouse Y-coordinate
                MOVE.B  MousDx,D2	    ; mouse Dx (byte)
                EXT.W   D2		    ; mouse Dx (integer)
                MOVE.B  MousDy,D3	    ; mouse Dy (byte)
                EXT.W   D3		    ; mouse Dy (integer)

Scale	        MOVE.W  D2,D4		    ; mouse Dx
                BGE.S   @1		    ; branch if >= 0
                NEG.W   D4		    ; ABS(mouse Dx)

@1	        MOVE.W  D3,D5		    ; mouse Dy
                BGE.S   @2		    ; branch if >= 0
                NEG.W   D5		    ; ABS(mouse Dy)

@2	        ADD.W   D5,D4		    ; ABS(Dx) + ABS(Dy)
                SUB.W   MousThresh,D4	    ;  - MouseThreshold
                BGT.S   Coarse		    ; branch if coarse movement

Fine	        ADD.W   D2,D0		    ; new X-coordinate (scale 1)
                MOVE.W  D3,D2		    ; save Dy
                ADD.W   D3,D3		    ; Dy*2
                ADD.W   D3,D3		    ; Dy*4
                ADD.W   D2,D3		    ; Dy*5
                ADDQ    #2,D3		    ; (Dy*5)+2
                BLT.S   @3		    ; branch if negative
                ADDQ    #3,D3		    ; (Dy*5)+5
@3	        ASR.W   #3,D3		    ; Dy*(5/8) with rounding
                ADD.W   D3,D1		    ; new Y-coordinate (scale 5/8)
                BRA.S   Bounds		    ; continue

Coarse	        ADD.W   D3,D1		    ; new Y-coordinate (scale 1)
                MOVE.W  D2,D3		    ; save Dx
                ADD.W   D3,D2		    ; Dx*2
                ADD.W   D3,D2		    ; Dx*3
                BLT.S   @4		    ; branch if negative
                ADDQ.W  #1,D2		    ; (Dx*3)+1
@4	        ASR.W   #1,D2		    ; Dx*(3/2) with rounding
                ADD.W   D2,D0		    ; new X-coordinate (scale 3/2)

Bounds	        TST.W   D0		    ; new X-coordinate >= 0
                BGE.S   @5		    ; branch if >= 0
                MOVE.W  #0,D0		    ; minimum X of 0

@5	        CMP.W   #MaxX,D0	    ; new X-coordinate <= 720
                BLE.S   @6		    ; branch if <= 720
                MOVE.W  #MaxX,D0	    ; maximum X of 720

@6	        TST.W   D1		    ; new Y-coordinate >= 0
                BGE.S   @7		    ; branch if >= 0
                MOVE.W  #0,D1		    ; minimum Y of 0

@7	        CMP.W   #MaxY,D1	    ; new Y-coordinate <= 364
                BLE.S   @8		    ; branch if <= 364
                MOVE.W  #MaxY,D1	    ; maximum Y of 364

@8	        MOVE.W  D0,MousX	    ; update Mouse X-coordinate
                MOVE.W  D1,MousY	    ; update Mouse Y-coordinate
                MOVE.W  D0,CrsrX	    ; also update cursor coordinates
                MOVE.W  D1,CrsrY
                MOVEM.L (SP)+,D1-D5	    ; restore registers
                RTS			    ; return

;-----------------------------------------------------------------------------
;
;  Routine to initialize mouse handling
;
;-----------------------------------------------------------------------------

MousInit
                MOVE.W  #360,MousX	    ; set inital mouse location
                MOVE.W  #182,MousY	    ;  to center of screen
                MOVE.W  #8,MousThresh	    ; set scaling threshold
                MOVEQ   #$7C,D0		    ; and enable mouse data
                BSR     COPSCMD
                RTS

;-----------------------------------------------------------------------------
;
;   Hardware Interface for the Cursor
;
;   Written by Rick Meyers
;   (c) Apple Computer Incorporated, 1983
;
;
;   The routines below provide an assembly language interface to the cursor.
;   Input parameters are passed in registers, output parameters are returned
;   in registers.  Unless otherwise noted, all registers are preserved.
;
;   The Cursor
;
;   The cursor is a small image that is displayed on the screen.  It's shape
;   is specified by two bitmaps, called 'data' and 'mask'.  These bitmaps are
;   16 bits wide and from 0 to 32 bits high.  The rule used to combine the
;   bits already on the screen with the data and mask is
;
;       screen <- (screen and (not mask)) xor data.
;
;   The effect is that white areas of the screen are replaced with the cursor
;   data.  Black areas of the screen are replaced with (not mask) xor data.
;   If the data and mask bitmaps are identical, the effect is to 'or' the data
;   onto the screen.
;
;   The cursor has both a location and a hotspot.  The location is a position
;   on the screen, with X-coordinates of 0 to 720 and Y-coordinates of 0 to 364 .
;   The hotspot is a position within the cursor bitmaps, with X- and Y-coordi-
;   nates ranging from 0 to 16.	 The cursor is displayed on the screen with it's
;   hotspot at location.  If the cursor's location is near an edge of the screen,
;   the cursor image may be partially or completely off the screen.
;
;------------------------------------------------------------------------------
;
;   Routine:    CursorInit
;   Arguments:  None
;   Function:   Sets up the initial defaults used by the ROM cursor.  Initial
;	        position is set for center of the screen.
;
;------------------------------------------------------------------------------

CursorInit      MOVE.W  #0,CrsrHotX	    ; cursor hotspot X-coordinate
                MOVE.W  #0,CrsrHotY	    ; cursor hotspot Y-coordinate
                MOVE.W  #16,CrsrHeight	    ; cursor hieght, 0-32
                MOVE.W  #360,CrsrX	    ; initial cursor X-coordinate
                MOVE.W  #182,CrsrY	    ; initial cursor Y-coordinate
                BSR.S   MousInit	    ; init mouse for cursor control
                RTS			    ; return

;-----------------------------------------------------------------------------
;
;   Cursor Hide and Display
;
;   Care must be taken when updating the screen image which is 'under' the
;   cursor.  The simplest approach is to remove the cursor from the screen
;   (hide), do the screen modification, then redisplay the cursor (display).
;
;   Each hide operation must be followed by a corresponding display
;   operation.  The operations are paired and can be nested.  The first of a
;   series of hides removes the cursor from the screen; it's corresponding
;   display redisplays the cursor.  Intervening operations have no apparent
;   effect.
;
;------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
;   Routine:    CursorHide
;   Arguments:  None
;   Function:   Remove the cursor from the screen.  Note that every call to
;	        CursorHide must be followed by exactly one call to CursorDisplay.
;---------------------------------------------------------------------------------

CursorHide      MOVEM.L D0-D1/A0-A1,-(SP)   ; save registers
                LEA     SavedData,A0	    ; saved data address
                MOVE.L  SavedAddr,A1	    ; saved data screen address
                MOVE.W  SavedRows,D0	    ; rows of saved data
                MOVE.W  #MaxX/8,D1	    ; bytes per row on screen
                BRA.S   @2		    ; test of rows=0

@1	        MOVE.L  (A0)+,(A1)	    ; from saved to screen
                ADD.W   D1,A1		    ; screen address of next row
@2	        DBF     D0,@1		    ; loop 'SavedRows' times

                MOVEM.L (SP)+,D0-D1/A0-A1   ; restore registers
                RTS			    ; return

;------------------------------------------------------------------------------
;
;   Routine:    CursorDisplay
;   Arguments:	  none
;   Function:   Redisplay the cursor.  Note that every call to CursorDisplay
;	        must be preceeded by exactly one call to CursorHide.
;
;   Register Assignments:
;
;       D0  --  saved data X-coordinate, cursor data
;       D1  --  saved data Y-coordinate, cursor mask
;       D2  --  left shift count
;       D3  --  32-bit mask
;       D4  --  rows of saved data
;       D5  --  bytes per row on screen
;
;       A0  --  saved data address
;       A1  --  saved data screen address
;       A2  --  cursor data address
;       A3  --  cursor mask address
;------------------------------------------------------------------------------

CursorDisplay   MOVEM.L D0-D5/A0-A3,-(SP)   ; save registers

                LEA     SavedData,A0	    ; saved data address
                MOVE.L  ScrnBase,A1	    ; screen memory address
                LEA     CrsrData,A2	    ; cursor data bitmap address
                LEA     CrsrMask,A3	    ; cursor mask bitmap address
                MOVE.W  CrsrHotX,D0	    ; cursor hotspot X-coordinate
                MOVE.W  CrsrHotY,D1	    ; cursor hotspot Y-coordinate
                MOVE.W  CrsrHeight,D4	    ; cursor height

;   Compute and bounds check the X-coordinate of the data under the cursor.

@11	        SUB.W   CrsrX,D0	    ;  cursor X-coordinate
                NEG.W   D0		    ;  - cursor hotspot X-coordinate
                MOVE.W  D0,D2		    ; upper left X-coordinate
                AND.W   #$000F,D2	    ; bit offset within word
                NEG.W   D2		    ;  negated and converted to
                ADD.W   #16,D2		    ;  left shift count
                CLR.L   D3		    ; 32-bit mask		        RM000
                NOT     D3		    ; D3 = $0000FFFF		        RM000
                LSL.L   D2,D3		    ;  shifted into position

                AND.W   #$FFF0,D0	    ; upper left X-coord rounded down
                BGE.S   @0		    ; branch if >= 0

                MOVE.W  #0,D0		    ; minimum upper left X-coord of 0
                LSL.L   #8,D3		    ; adjust 32-bit mask
                LSL.L   #8,D3		    ; adjust 32-bit mask
                ADD.W   #16,D2		    ; adjust left shift count

@0	        CMP.W   #MaxX-32,D0	    ; upper left X-coord <= 720-32
                BLE.S   @2		    ; branch if <= 720-32

                CMP.W   #MaxX,D0	    ; cursor off right edge ?
                BNE.S   @1		    ; branch if not off right edge
                MOVEQ   #0,D3		    ; mask off all bits

@1	        MOVE.W  #MaxX-32,D0	    ; maximum X-coord of 720-32
                LSR.L   #8,D3		    ; adjust 32-bit mask
                LSR.L   #8,D3		    ; adjust 32-bit mask
                ADD.W   #16,D2		    ; adjust left shift count

;   Compute and bounds check the Y-coordinate of the data under the cursor.

@2	        SUB.W   CrsrY,D1	    ; cursor Y-coordinate
                NEG.W   D1		    ;  - cursor hotspot Y-coordinate
                BGE.S   @3		    ; branch if upper left Y >= 0

                ADD.W   D1,D4		    ; decrease rows of saved data
                ADD.W   D1,D1		    ; double for byte count
                SUB.W   D1,A2		    ; increase cursor data address
                SUB.W   D1,A3		    ; increase cursor mask address
                MOVE.W  #0,D1		    ; minimum upper left Y of 0
                BRA.S   @4		    ; continue

@3	        MOVE.W  #MaxY,D5	    ; maximum Y-coordinate
                SUB.W   D4,D5		    ;  - cursor height
                CMP.W   D5,D1		    ; cursor bottom <= 364-CrsrHeight ?
                BLE.S   @5		    ; branch if <= 364-CrsrHeight

                MOVE.W  #MaxY,D4	    ; last row on screen
                SUB.W   D1,D4		    ; adjust rows of saved data

@4	        TST.W   D4		    ; rows of saved data >= 0 ?
                BGE.S   @5		    ; branch if	 >= 0
                MOVE.W  #0,D4		    ; minimum rows of saved data

@5	        MOVE.W  D0,SavedX	    ; saved data X-coordinate
                MOVE.W  D1,SavedY	    ; saved data Y-coordinate
                MOVE.W  D4,SavedRows	    ; rows of saved data

;   Display the cursor on the screen.

                LSR.W   #3,D0		    ; convert X-coord to bytes
                ADD.W   D0,A1		    ;  and add to screen address
                MOVEQ   #MaxX/8,D5	    ; bytes per row on screen
                MULU    D5,D1		    ;  * Y-coord
                ADD.L   D1,A1		    ;  added to screen address
                MOVE.L  A1,SavedAddr	    ; saved data screen address
                BRA.S   @7		    ; test for rows=0

 @6	        MOVE.W  (A2)+,D0	    ; cursor data
                ROL.L   D2,D0		    ; shift to proper bit position
                AND.L   D3,D0		    ; eliminate unwanted bits
                MOVE.W  (A3)+,D1	    ; cursor mask
                ROL.L   D2,D1		    ; shift to proper bit position
                AND.L   D3,D1		    ; eliminate unwanted bits
                NOT.L   D1		    ; invert cursor mask

                MOVE.L  (A1),(A0)+	    ; from screen to saved data
                AND.L   D1,(A1)		    ; screen and (not mask)
                EOR.L   D0,(A1)		    ;  xor cursor data
                ADD.W   D5,A1		    ; screen address of next row
@7	        DBF     D4,@6		    ; loop 'SavedRows' times

                MOVEM.L (SP)+,D0-D5/A0-A3   ; restore registers
                RTS			    ; return

        .ENDC		        ;{USERINT}
        .IF  AAPL = 1
        .PAGE
;-------------------------------------------------------------------------
;  Testing complete - reset default NMI vector and display ROM #'s
;-------------------------------------------------------------------------

NMISET
        LEA     NMI,A6	        ;restore NMI ptr
        MOVE.L  A6,NMIVCT

;  Reset to first video page (for compatibility w/ old software)

RSTOR   MOVE.L  MINMEM,D0       ;get starting memory address
        LSR.L   #8,D0	        ;convert to 32K multiple
        LSR.L   #7,D0
        MOVE    D0,VIDLTCH      ;and set video page latch
        CLR.L   SCRNBASE        ;and set base screen address

;  Display ROM version #'s and OK msg if no errors

        TST.L   STATUS	        ;any errors?
        BNE.S   @1	        ;skip if yes
        LEA     INITMSG,A3      ;else display OK msg
        BSR     DSPMSGR
@1      BSR     DSPROMS	        ;display ROM versions

;  And restore ROM Monitor environment

        MOVE.L  #STKBASE,SP     ;RESTORE STACK POINTER
        LEA     BERR,A3	        ;and bus error vector
        MOVE.L  A3,BUSVCTR
;	 MOVEQ	 #FIRSTROW,D5	 ;AND CURSOR PTRS
;	 MOVEQ	 #FIRSTCOL,D6

        .PAGE
;-------------------------------------------------------------------------
; APPLE MONITOR CODE - ENABLES COMMUNICATION VIA APÐLE ][
@4	        TST.W   D4		    ; rows of saved data >= 0 ?
                BGE.S   @5		    ; branch if	 >= 0
                MOVE.W  #0,D4		    ; minimum rows of saved data

@5	        MOVE.W  D0,SavedX	    ; saved data X-coordinate
                MOVE.W  D1,SavedY	    ; saved data Y-coordinate
                MOVE.W  D4,SavedRows	    ; rows of saved data

;   Display the cursor on the screen.

                LSR.W   #3,D0		    ; convert X-coord to bytes
                ADD.W   D0,A1		    ;  and add to screen address
                MOVEQ   #MaxX/8,D5	    ; bytes per row on screen
                MULU    D5,D1		    ;  * Y-coord
                ADD.L   D1,A1		    ;  added to screen address
                MOVE.L  A1,SavedAddr	    ; saved data screen address
                BRA.S   @7		    ; test for rows=0

 @6	        MOVE.W  (A2)+,D0	    ; cursor data
                ROL.L   D2,D0		    ; shift to proper bit position
                AND.L   D3,D0		    ; eliminate unwanted bits
                MOVE.W  (A3)+,D1	    ; cursor mask
                ROL.L   D2,D1		    ; shift to proper bit position
                AND.L   D3,D1		    ; eliminate unwanted bits
                NOT.L   D1		    ; invert cursor mask

                MOVE.L  (A1),(A0)+	    ; from screen to saved data
                AND.L   D1,(A1)		    ; screen and (not mask)
                EOR.L   D0,(A1)		    ;  xor cursor data
                ADD.W   D5,A1		    ; screen address of next row
@7	        DBF     D4,@6		    ; loop 'SavedRows' times

                MOVE.B  #1,CrsrVisible	    ; cursor visible
                MOVEM.L (SP)+,D0-D5/A0-A3   ; restore registers

@8	        MOVE.B  #0,CrsrBusy	    ; cursor change complete
                RTS			    ; return

;------------------------------------------------------------------------------
;
;   Routine:    CursorInit
;   Arguments:	  none
;   Function:   Definately redisplay the cursor, independent of previous calls
;	        to CursorHide, CursorShield and CursorObscure.  Also sets up
;	        the initial defaults used by the ROM cursor.
;

CursorInit      MOVE.L  D0,-(SP)	    ; save registers
                MOVE.W  #0,CrsrHotX	    ; cursor hotspot X-coordinate
                MOVE.W  #0,CrsrHotY	    ; cursor hotspot Y-coordinate
                MOVE.W  #16,CrsrHeight	    ; cursor hieght, 0-32
                MOVE.W  #0,CrsrX	    ; initial cursor X-coordinate
                MOVE.W  #0,CrsrY	    ; initial cursor Y-coordinate
                MOVE.B  #0,CrsrObscured	    ; 0=not obscured, else=obscured
                MOVE.B  #0,CrsrHidden	    ; <= 0 implies hidden
                MOVE.B  #0,CrsrVisible	    ; 0 = not visible, else=visible
                MOVE.B  #1,CrsrTracking	    ; enable cursor tracking
                BSR     CursorDisplay	    ; display the cursor
                MOVE.L  (SP)+,D0	    ; restore registers
                RTS			    ; return

        .ENDC		        ;{USERINT}
        .IF  AAPL = 1
        .PAGE
;-------------------------------------------------------------------------
;  Testing complete - reset default NMI vector and display ROM #'s
;-------------------------------------------------------------------------

NMISET
        MOVE.B  (A0)+,D0        ; ELSE EXAMINE MEMORY
        BSR4    CHKSUM	        ;ADD TO CHECKSUM
        BSR2	 PUT		 ; AND SEND TO PIA
        BRA.S   EXMLOOP	        ; LOOP TILL COUNT EXHAUSTED

CMDDONE BSR4    ECHOSUM	        ;RETURN CHECKSUM
        BRA     GETCMD	        ;AND GO WAIT FOR NEXT CMD


DEPOSIT BSR4    CHKHDR	        ;GO GET AND CHECK ADDRESS, COUNT FIELDS
        CLR.L   D7	        ;CLEAR FOR DATA CHECKSUM USE
DEMLOOP SUBQ    #1,D1	        ; DEC COUNT
        BCS     CMDDONE	        ; IF DONE, SEND REPLY
        BSR2	 GET		 ; ELSE GET BYTE FROM PIA
        MOVE.B  D0,(A0)	        ; AND DEPOSIT INTO MEMORY
        CMPA.L  #IOSPACE,A0     ;CHECK IF IN IO SPACE
        BCC.S   @2	        ;SKIP READ IF YES
        MOVE.B  (A0)+,D0        ;ELSE READ BACK
@2      BSR4    CHKSUM	        ;UPDATE CHECKSUM
        BRA.S   DEMLOOP	        ; LOOP TILL COUNT EXHAUSTED

;
;       READ - read a location (over and over)
;

READ    BSR4	 GETADDR	 ; fetch address to A0
        BSR4	 GETKNT		 ;  and count to D1
@1      MOVE.B  (A0),D0	        ; read once
        SUBQ    #1,D1
        BNE     @1	        ;  and loop til done
        BRA     REPLY	        ; reply is fetched byte
;
;       WRITE - write fixed location (many times)
;

WRITE   BSR4    GETADDR	        ; fetch address
        BSR4    GETKNT	        ;  and count
        BSR2    GET	        ;  and data byte
@1      MOVE.B  D0,(A0)	        ; do the write
        SUBQ    #1,D1	        ; and loop until done
        BNE.S   @1
        BRA     REPLY

;
;       RDMULTI - Read sweep of locations
;

RDMULTI BSR4    GETADDR	        ; get params
        BSR4    GETKNT
@1      TST     (A0)+
        SUBQ    #1,D1
        BNE     @1
        BRA     REPLY

;
;       WRMULTI - Write sweep of locations
;

WRMULTI BSR4    GETADDR
        BSR4    GETKNT
@1      MOVE    A0,(A0)+
        SUBQ    #1,D1
        BNE     @1
        BRA     REPLY
;
;       Routine to do check command, address and count fields
;

CHKHDR  MOVE.L  A4,A3	        ;SAVE RETURN ADDRESS
        BSR4    GETADDR	        ;GET 4 BYTE ADDRESS IN A0
        BSR4    GETKNT	        ;GET 2 BYTE COUNT IN D1
        CLR.L   D0
        MOVE.L  A0,D2	        ;GET ADDRESS DATA
        MOVEQ   #8,D3	        ;SET SHIFT COUNT
        MOVEQ   #4,D4	        ;SET LOOP COUNT
@1      ROL.L   D3,D2	        ;GET ADDRESS BYTE
        MOVE.B  D2,D0
        BSR4    CHKSUM	        ;GO ADD TO CHECKSUM
        SUBQ    #1,D4
        BNE.S   @1	        ;LOOP UNTIL DONE

        CLR.L   D2
        MOVE    D1,D2	        ;GET COUNT DATA
        ROR     D3,D2	        ;GET HIGH BYTE
        MOVE.B  D2,D0
        BSR4    CHKSUM	        ;ADD TO CHECKSUM
        ROL     D3,D2	        ;GET LOW BYTE
        MOVE.B  D2,D0
        BSR4    CHKSUM	        ;ADD TO CHECKSUM

        BSR4    ECHOSUM	        ;ECHO RESULT TO APPLE
        BSR2    GET	        ;GET APPLE'S REPLY
        TST.B   D0	        ;OK?
        BNE     GETCMD	        ;ABORT IF NO
        MOVE.L  A3,A4	        ;ELSE GET RTRN ADDRESS
        RTS4

;
;       Routine to calculate checksum
;       Assumes D0 = byte to add to checksum
;	        D7 = checksum word
;

CHKSUM  ANDI    #$00FF,D0       ;ENSURE VALID DATA
        ADD     D0,D7	        ;ADD TO CHECKSUM
        ROL     #1,D7	        ;ROTATE FOR NEXT USE
        RTS4

;
;       Routine to send checksum word in D7 to APPLE
;

ECHOSUM MOVE    D7,D0	        ;GET IT
        BSR2    PUT	        ;OUTPUT LOW BYTE
        LSR     #8,D0
        BSR2    PUT	        ;OUTPUT HIGH BYTE
        RTS4

        .ENDC
        .ENDC		        ;{ROM4K}

